import datetime
import json
import os
import Logger

import xlrd
from xlrd import Book
from xlrd.sheet import Sheet
import Util

from ExcelData import ExcelData, TableData, FieldData, FieldType, EData, LoadConfig, IndexType, Platform, Union
from typing import List, Dict
from lib import CompatibleCRC32 as CRC32


class JsonGenerator:
    log: Logger
    edata: EData

    lastJsonChangesCount: int = 0
    """本次变动json文件数"""
    lastExportVersion: str = None
    lastExportTime: datetime = None
    lastExportTables: List[TableData] = None
    lastErrorCount: int = 0;

    existTables: List[TableData] = None
    """存在的表格(统计存在的表格)"""

    def __init__(self):
        self.log = Logger.Logger()
        self.edata = EData()

    def exportFile(self, jsonGenPath: str, exportVersion: str, exportTime: datetime, isGenerateAll: bool):
        self.lastExportTime = exportTime
        self.lastExportVersion = exportVersion
        self.lastExportTables = []
        self.existTables = []
        self.lastJsonChangesCount = 0
        if not jsonGenPath.endswith("\\"):
            jsonGenPath += "\\"
        # 删除之前生成的.java文件
        # filelist = os.listdir(self.edata.setting.defDirJsonFile)
        # for filename in filelist:
        #     filepath = self.edata.setting.defDirJsonFile + filename
        #     if os.path.isfile(filepath):
        #         if filename.endswith(".json") and not filename == self.edata.setting.defDefineJsonFile:
        #             os.remove(filepath)
        #             print("删除旧文件：" + filepath)

        strIgnore = self.edata.setting.readerCfgIgnoreColumn.replace("，", ",")
        arrIgnore = strIgnore.split(",")
        for i in range(0, len(arrIgnore)):
            arrIgnore[i] = arrIgnore[i].strip().lower()
        for excel in self.edata.excelDatas.selectedDatas.values():
            if not os.path.isfile(excel.filePath):
                self.log.logError("文件打开错误！ 文件名：" + excel.filePath)
                continue
            workbook: Book = xlrd.open_workbook(excel.filePath)
            # 遍觅每个excel文件
            for table in excel.tables.values():
                self.existTables.append(table)
                # 检查是否已勾选导出
                if isGenerateAll == False:
                    if not table.isListChecked:
                        continue
                for sname in workbook.sheet_names():
                    # 遍觅每个sheet
                    if table.sheetName == sname:
                        # 正式开始读取数据
                        jsonData = {}
                        sheet: Sheet = workbook.sheet_by_name(sname)
                        jsonRows = []
                        count: int = 0
                        for r in range(table.fieldDataRowIndex, sheet.nrows):
                            # 遍觅每一行数据
                            row = sheet.row(r)
                            jsonField = {}
                            isSkipField = False
                            skipStr: str = row[0].value
                            skipStr = skipStr.strip(" ").lower()
                            # 跳过不导出字段
                            isSkip: bool = False
                            for ignore in arrIgnore:
                                if skipStr == ignore:
                                    isSkip = True
                            if isSkip:
                                continue

                            for fData in table.fields:
                                # 根据预分析的field.colunmOrd，知道哪一列是数据列，直接获取不遍觅
                                field: FieldData = fData
                                objValue = row[field.colunmOrd].value
                                strValue: str = str(objValue)
                                if field.canNotEmpty and strValue.strip() == "":
                                    # 已经没有数据了，不导出
                                    isSkipField = True
                                    continue

                                # 整形数据赋值
                                if field.fieldType == FieldType.Integer:
                                    if objValue is None or objValue == "" or strValue.strip() == "":
                                        jsonField[field.fieldKey] = 0
                                    else:
                                        try:
                                            value = int(objValue)
                                            jsonField[field.fieldKey] = value
                                        except ValueError:
                                            self.lastErrorCount += 1
                                            jsonField[field.fieldKey] = 0;
                                            self.log.logWarning(
                                                "导出 [" + table.jsonName + "]" + table.sheetName + " 出错")
                                            self.log.logWarning("整数(int)数据类型转换错误：" + str(r)
                                                                + "行，[" + fData.fieldKey + "]" + fData.fieldName)

                                # 浮点数据赋值
                                if field.fieldType == FieldType.Float:
                                    if objValue is None or objValue == "" or strValue.strip() == "":
                                        jsonField[field.fieldKey] = 0.0
                                    else:
                                        try:
                                            # jsonField[field.fieldKey] = objValue
                                            value = float(objValue)
                                            jsonField[field.fieldKey] = objValue
                                            # jsonField[field.fieldKey] = value
                                        except ValueError:
                                            self.lastErrorCount += 1
                                            jsonField[field.fieldKey] = 0.0;
                                            self.log.logWarning(
                                                "导出 [" + table.jsonName + "]" + table.sheetName + " 出错")
                                            self.log.logWarning("浮点(float)数据类型转换错误：" + str(r)
                                                                + "行，[" + fData.fieldKey + "]" + fData.fieldName)

                                # 64位整形与浮点数据赋值（以字符形式）
                                if field.fieldType == FieldType.Long or field.fieldType == FieldType.Double:
                                    if objValue is None or objValue == "":
                                        jsonField[field.fieldKey] = "0"
                                    else:
                                        jsonField[field.fieldKey] = str(objValue)

                                # 字符串赋值
                                if field.fieldType == FieldType.String:
                                    jsonField[field.fieldKey] = str(objValue)

                                # 布尔赋值
                                if field.fieldType == FieldType.Boolean:
                                    val = str(objValue).lower().strip()
                                    if val == "true" or val == "1" or val == "t" or val == "真" or val == "是":
                                        jsonField[field.fieldKey] = True
                                    else:
                                        jsonField[field.fieldKey] = False

                                # 特殊List或Map字段
                                if field.fieldType.isArrTypeOrMap():
                                    jsonField[field.fieldKey + "OrgStr"] = str(objValue)

                            if not isSkipField:
                                jsonRows.append(jsonField)
                                count += 1
                        # json表头
                        headData: dict = dict()
                        headData["count"] = count
                        jsonData["header"] = headData
                        # 表格数据
                        jsonData["data"] = jsonRows
                        # jsonHashStr = json.dumps(jsonData, ensure_ascii=False, indent=2)
                        # md5: str = Util.getMd5(jsonHashStr)
                        # headData["version"] = exportVersion
                        # headData["date"] = Util.getDateTimeString(exportTime)
                        # headData["md5"] = md5
                        # table.lastMd5 = md5
                        # headData["author"] = Util.getComputerName()
                        jsonStr = json.dumps(jsonData, ensure_ascii=False, indent=2)
                        # md5: str = Util.getMd5(jsonStr)
                        crc: str = CRC32.get32BitHex(jsonStr)
                        if crc == table.lastMd5:
                            pass
                        else:
                            table.lastExecuteTime = exportTime
                            table.lastMd5 = crc
                            table.lastVersion = self.lastExportVersion
                            self.lastJsonChangesCount += 1
                        # print(jsonStr)
                        if jsonStr is not None:
                            filepath = jsonGenPath + table.jsonName + ".json"
                            f = open(filepath, 'w', encoding='utf8')
                            f.write(jsonStr)
                            f.close()
                            self.log.logSuccess(
                                "导出 [" + table.jsonName + "]" + table.sheetName + " 成功，共导出数据行：" + str(count))
                            self.lastExportTables.append(table)

        return self.lastJsonChangesCount

    def getLastVersion(self, jsonGenPath: str):
        version: int = 0
        jsonData: {} = None
        if not jsonGenPath.endswith("\\"):
            jsonGenPath += "\\"
        filepath = jsonGenPath + self.edata.setting.defDefineJsonFile
        if os.path.isfile(filepath):
            try:
                f = open(filepath, 'r', encoding='utf8')
                jsonData = json.load(f)
            except Exception as e:
                jsonData = None
                self.log.logError("打开旧定义文件_definition.json出错!")
            finally:
                f.close()
        # 没有读取旧配置则创建空白的
        if jsonData is None:
            return version

        headData: dict = jsonData.get("header")
        verStr: str = headData["version"]
        version = Util.getVersionCode(verStr)

        return version

    def preloadFileSetting(self, jsonGenPath: str):
        jsonData: {} = None
        if not jsonGenPath.endswith("\\"):
            jsonGenPath += "\\"
        filepath = jsonGenPath + self.edata.setting.defDefineJsonFile
        if os.path.isfile(filepath):
            try:
                f = open(filepath, 'r', encoding='utf8')
                jsonData = json.load(f)
            except Exception as e:
                jsonData = None
                self.log.logError("打开旧定义文件_definition.json出错!")
            finally:
                f.close()
        # 没有读取旧配置则创建空白的
        if jsonData is None:
            jsonData = {}

        tablesData: List[TableData] = None
        tmpTablesData = jsonData.get("tables")
        if type(tmpTablesData) is list:
            tablesData = tmpTablesData
        if tablesData is None:
            tablesData = []

        for excel in self.edata.excelDatas.selectedDatas.values():
            if not os.path.isfile(excel.filePath):
                # self.log.logError("文件打开错误！ 文件名：" + excel.filePath)
                continue
            # 遍觅每个excel文件
            for table in excel.tables.values():
                for dfTable in tablesData:
                    jsonName: str = dfTable.get("jsonName")
                    # 找到原definition文件中记录对应的json文件的信息
                    if table.jsonName == jsonName:
                        version: str = dfTable.get("version")
                        md5: str = dfTable.get("md5")
                        strDate: str = dfTable.get("date")
                        table.lastMd5 = md5
                        table.lastVersion = version

    def exportFileSetting(self, jsonGenPath: str, existTables: List[str], isGenerateAll: bool):
        # log: Logger = Logger.Logger()
        # edata: EData = EData()
        jsonData: {} = None
        if not jsonGenPath.endswith("\\"):
            jsonGenPath += "\\"
        filepath = jsonGenPath + self.edata.setting.defDefineJsonFile
        if os.path.isfile(filepath):
            try:
                f = open(filepath, 'r', encoding='utf8')
                jsonData = json.load(f)
            except Exception as e:
                jsonData = None
                self.log.logError("打开旧定义文件_definition.json出错!")
            finally:
                f.close()
        # 没有读取旧配置则创建空白的
        if jsonData is None:
            jsonData = {}

        headData: dict = jsonData.get("header")
        if headData is None:
            headData: dict = dict()
            jsonData["header"] = headData
        headData["version"] = self.lastExportVersion
        headData["date"] = Util.getDateTimeString(self.lastExportTime)
        headData["author"] = Util.getComputerName()

        # 读取旧的defines
        loadconfigs: [] = jsonData.get("defines")
        if loadconfigs is None:
            loadconfigs = []
            jsonData["defines"] = loadconfigs

        # 删除过期的loadconfig定义
        delConfigs: [] = []
        for config in loadconfigs:
            jsonName: str = config.get("loadJson")
            isDelete: bool = True
            if not jsonName is None:
                for tableName in existTables:
                    if jsonName.lower() == tableName:
                        isDelete = False
                        break
            if isDelete:
                delConfigs.append(config)
        for delConfig in delConfigs:
            loadconfigs.remove(delConfig)

        tablesData: List[TableData] = None
        tmpTablesData = jsonData.get("tables")
        if type(tmpTablesData) is list:
            tablesData = tmpTablesData
        if tablesData is None:
            tablesData = []
            jsonData["tables"] = tablesData

        # 删除过期的table定义
        delTables: [] = []
        for table in tablesData:
            jsonName: str = table.get("jsonName")
            isDelete: bool = True
            if not jsonName is None:
                for tableName in existTables:
                    if jsonName.lower() == tableName:
                        isDelete = False
                        break
            if isDelete:
                delTables.append(table)
        for delTable in delTables:
            tablesData.remove(delTable)

        contactAllMd5: str = ""

        for excel in self.edata.excelDatas.selectedDatas.values():
            for table in excel.tables.values():
                # 检查是否已勾选导出
                if isGenerateAll == False:
                    if not table.isListChecked:
                        continue
                # 处理更新表格配置
                tb: dict = dict()
                tb["jsonName"] = table.jsonName
                tb["comment"] = table.sheetName
                tb["md5"] = table.lastMd5
                contactAllMd5 += table.lastMd5
                tb["date"] = Util.getDateTimeString(table.lastExecuteTime)
                tb["version"] = table.lastVersion  # self.lastExportVersion
                tb["author"] = Util.getComputerName()
                oldIndex: int = -1
                # oldTbData:TableData = tablesData.get(table.jsonName)
                # tablesData[table.jsonName] = tb
                for i in range(0, len(tablesData)):
                    oldTbData = tablesData[i]
                    if oldTbData["jsonName"] == table.jsonName:
                        oldIndex = i
                if oldIndex < 0:
                    tablesData.append(tb)
                else:
                    tablesData[oldIndex] = tb

                # 处理加载配置
                for loadCfg in table.loadConfigs:
                    cfg: dict = self.getLoadConfigDict(loadCfg, table)
                    oldIndex: int = -1
                    for i in range(0, len(loadconfigs)):
                        oldCfgData = loadconfigs[i]
                        if oldCfgData["dataFullName"] == loadCfg.getDataFullName(table):
                            oldIndex = i
                    if oldIndex < 0:
                        loadconfigs.append(cfg)
                    else:
                        loadconfigs[oldIndex] = cfg

        headData["count"] = len(loadconfigs)
        headData["md5"] = CRC32.get32BitHex(contactAllMd5)  # Util.getMd5(contactAllMd5)
        # jsonData["header"] = headData
        # jsonData["tables"] = tablesData
        # jsonData["defines"] = loadconfigs
        jsonStr = json.dumps(jsonData, ensure_ascii=False, indent=2)
        if jsonStr is not None:
            # filepath = 'output/' + self.edata.setting.defDefineJsonFile
            try:
                f = open(filepath, 'w', encoding='utf8')
                f.write(jsonStr)
            except Exception as e:
                self.log.logError("写入定义文件_definition.json出错!")
            finally:
                f.close()
            self.log.logSuccess("导出定义文件成功，共导出数据行：" + str(len(loadconfigs)))

        return True

    def getLoadConfigDict(self, loadCfg: LoadConfig, table: TableData):
        cfg: dict = dict()
        keysets = []
        for key in loadCfg.keyFields:
            keyset = {}
            keyset["name"] = key.fieldKey
            keyset["type"] = key.fieldType.name
            keysets.append(keyset)
        cfg["keys"] = keysets
        cfg["mainKey"] = keysets[0]["name"]
        cfg["mainKeyType"] = keysets[0]["type"]
        cfg["type"] = loadCfg.type.name
        cfg["loadWeight"] = loadCfg.loadWeight
        cfg["loadJson"] = table.jsonName
        cfg["comment"] = table.sheetName
        cfg["className"] = table.getClassname()
        cfg["dataName"] = loadCfg.getDataName(table)
        cfg["dataFullName"] = loadCfg.getDataFullName(table)
        # 导出平台资料
        platforms: [] = []
        for platform in loadCfg.exportPlatforms.values():
            platforms.append(platform.name)
        cfg["platforms"] = platforms
        # 导出关联字段
        unions: [] = []
        for union in loadCfg.unions:
            u: dict = dict()
            u["key"] = union.myKey
            u["unionTable"] = union.unionTableName
            u["unionKey"] = union.unionKey
            unions.append(u)
        cfg["unions"] = unions
        # 导出组别
        cfg["group"] = self.edata.setting.groupPrefix + loadCfg.group
        # 导出关联字段
        loadRelations: List[str] = []
        for strRelation in loadCfg.loadRelations:
            frTable: TableData = None
            for rExcel in self.edata.excelDatas.selectedDatas.values():
                for rTable in rExcel.tables.values():
                    if rTable.sheetName == strRelation:
                        frTable = rTable
            if frTable is None:
                self.log.logError("严重错误！配置中关联加载中找不到关联表格：" + strRelation)
                return False
            loadRelations.append(frTable.jsonName)
        cfg["loadRelations"] = loadRelations
        return cfg
